{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Measuring SNR of Target\n",
    "\n",
    "Supported setups:\n",
    "\n",
    "SCOPES:\n",
    "\n",
    "* OPENADC\n",
    "* CWNANO\n",
    "\n",
    "PLATFORMS:\n",
    "\n",
    "* CWLITEARM\n",
    "* CWLITEXMEGA\n",
    "* CWNANO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "CRYPTO_TARGET = 'TINYAES128C'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../hardware/victims/firmware/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Signal to Noise Ratio (SNR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In general, the \"Signal to Noise Ratio\" is defined as:\n",
    "\n",
    "$$SNR = \\frac{Var(Signal)}{Var(Noise)}$$\n",
    "\n",
    "This is to say the variance in the signal measured compared to the variance in the noise measured. You will often see SNR expressed in dB, which is a logarithmic scale. In which case the conversion is simply done as:\n",
    "\n",
    "$$SNR_{dB} = 20log(SNR)$$\n",
    "\n",
    "Note this assumes our measurements were *voltages* -- the 20 infront of the log is done to represent the fact that SNR is typically referencing the power difference between signal and noise. The power across a resistor would be equal to the square of the voltage, so we should actually have:\n",
    "\n",
    "$$SNR_{dB} = 10log\\left(  \\left( \\frac{Var(Signal)}{Var(Noise)}\\right)^2\\right) = 20log\\left( \\frac{Var(Signal)}{Var(Noise)}\\right)$$\n",
    "\n",
    "### What's the Signal?\n",
    "\n",
    "The above was very easy to right out. But what is the signal, and what is the noise? The signal is going to be the leakage we measured *based on some leakage function*, and the noise will be the *noise inherent in the measurement not caused by the leakage*.\n",
    "\n",
    "The easiest way to do this will be to find the average trace for each leakage \"group\". If using the Hamming weight leakage model, this means we have 9 traces (one for each HW). If we used classic DPA we would have two groups (one for each bit).\n",
    "\n",
    "Within each group, we can measure the noise. We don't actually measure across *all* groups since then we would have the leakage contributing to our \"noise\". We want to get a measure of only the noise, not variance being caused by the signal.\n",
    "\n",
    "### Outline of this Tutorial\n",
    "\n",
    "As usual, we're going to first go through a detailed example. We'll then give you some quick cheater functions to calculate the SNR based on leakage models built into ChipWhisperer (yay!). This makes it easy to quickly compare leakage models.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Capturing Power Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The capture part is the same as previous tutorials. We include it here to make it interactive."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll use some helper scripts to make setup and programming easier. If you're using an XMEGA or STM (CWLITEARM) target, binaries with the correct should be setup for you:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"Helper_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = \"../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex\".format(PLATFORM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Capturing Traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below you can see the capture loop. The main body of the loop loads some new plaintext, arms the scope, sends the key and plaintext, then finally records and appends our new trace to the `traces[]` list. At the end, we convert the trace data to numpy arrays, since that's what we'll be using for analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Capture Traces\n",
    "from tqdm import tnrange\n",
    "import numpy as np\n",
    "import time\n",
    "\n",
    "\n",
    "ktp = cw.ktp.Basic()\n",
    "\n",
    "traces = []\n",
    "N = 1000  # Number of traces\n",
    "if PLATFORM == \"CWNANO\":\n",
    "    N = 1500\n",
    "\n",
    "for i in tnrange(N, desc='Capturing traces'):\n",
    "    key, text = ktp.next()\n",
    "    \n",
    "    trace = cw.capture_trace(scope, target, text, key)\n",
    "    if trace is None:\n",
    "        continue\n",
    "    traces.append(trace)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have our traces, we can also plot them using Bokeh:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews as hv\n",
    "hv.extension('bokeh')\n",
    "hv.Curve(traces[0].wave).opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup the connection to the target and scope\n",
    "scope.dis()\n",
    "target.dis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## SNR Calculation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first thing we'll do is spread the traces into the \"groups\". Remembering the examples of plotting Hamming Weight, we went over how the leakage model is used. All we need to do here is perform the same sort of operation, except we'll take the mean of each group as well. The following will end up making an array, `hwmean[]` that contains a mean for HW=0, HW=1, etc.:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbox = (\n",
    "    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\n",
    "    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\n",
    "    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\n",
    "    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\n",
    "    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\n",
    "    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\n",
    "    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\n",
    "    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\n",
    "    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\n",
    "    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\n",
    "    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\n",
    "    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\n",
    "    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\n",
    "    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\n",
    "    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\n",
    "    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16)\n",
    "\n",
    "HW = [bin(n).count(\"1\") for n in range(0, 256)]\n",
    "\n",
    "def intermediate(pt, key):\n",
    "    return sbox[pt ^ key]\n",
    "\n",
    "#SNR of byte 0\n",
    "bnum = 0\n",
    "\n",
    "#Length of one trace\n",
    "npoints = len(traces[0].wave)\n",
    "\n",
    "hwarray = [[], [], [], [], [], [], [], [], []]\n",
    "\n",
    "#For each byte we are looking at - let's split into multiple groups\n",
    "for tnum in range(0, len(traces)):\n",
    "    hw_of_byte = HW[intermediate(traces[tnum].textin[bnum], traces[tnum].key[bnum])]\n",
    "    hwarray[hw_of_byte].append(traces[tnum].wave)\n",
    "    \n",
    "hwmean = np.zeros((9, npoints))\n",
    "    \n",
    "for i in range(0, 9):\n",
    "    hwmean[i] = np.mean(hwarray[i], axis=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We could plot those figures, and see the same sort of information from the HW plot lab too. We've done this a slightly different way, so you might find it even easier to see the difference here. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve = hv.Curve(hwmean[8],label=\"HW=0\")\n",
    "for i in range(0, 9):\n",
    "    curve *= hv.Curve(hwmean[8-i],label=\"HW={}\".format(8-i))\n",
    "curve.opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An important thing to note - the number of traces in each group will not be uniform! This is beacuse many values have a HW of 4 (11110000, 10101010, etc) but only one option has a HW of 0 or 8."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for t in range(0, 9):\n",
    "    print(\"HW %d has %d traces\"%(t, len(hwarray[t])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That last point is important, as we're going to use only one group for now to calculate the noise. Here I've selected `hwarray[4]` for example. We don't want to calculate across the entire trace set as it will include the signal variance (hint - you can test this by changing the variance calculation to be done over the `traces` variable).\n",
    "\n",
    "We also need to REMOVE any groups with zero traces. They will ruin our variance calculation (since there is no data to calculate variance over). This can also be done by simply adding traces to the capture side too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inc_list = []\n",
    "for i in range(0, len(hwarray)):\n",
    "    if len(hwarray[i]) > 0:\n",
    "        inc_list.append(i)\n",
    "        \n",
    "hwmean_valid = hwmean[inc_list]\n",
    "\n",
    "signal_var = np.var(hwmean_valid, axis=0)\n",
    "noise_var_onehw = np.var(hwarray[4], axis=0)\n",
    "\n",
    "snr = signal_var / noise_var_onehw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.Curve(20 * np.log(snr)).opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automated SNR Plotting"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Luckily, you can do this SNR plotting pretty easily. The following code shows off the ChipWhisperer function for doing so, based on a leakage model (same one as used by CPA attack)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import chipwhisperer.analyzer as cwa\n",
    "\n",
    "leak_model = cwa.leakage_models.sbox_output\n",
    "\n",
    "snrdb = cwa.calculate_snr(traces, leak_model=leak_model)\n",
    "\n",
    "hv.Curve(snrdb).opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following shows usage with a project file for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import chipwhisperer as cw\n",
    "import chipwhisperer.analyzer as cwa\n",
    "\n",
    "project = cw.create_project(\"projects/snr\")\n",
    "project.traces.extend(traces)\n",
    "\n",
    "leak_model = cwa.leakage_models.sbox_output\n",
    "\n",
    "snrdb = cwa.calculate_snr(project.traces, leak_model=leak_model)\n",
    "hv.Curve(snrdb).opts(height=600, width=600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tests"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_snr = -100\n",
    "good_snr = 40\n",
    "if PLATFORM == \"CWLITEARM\":\n",
    "    good_snr = 40\n",
    "elif PLATFORM == \"CWNANO\":\n",
    "    good_snr = -5\n",
    "elif PLATFORM == \"CWLITEXMEGA\":\n",
    "    good_snr = 25\n",
    "for point in snrdb:\n",
    "    if point > max_snr:\n",
    "        max_snr = point\n",
    "assert max_snr > good_snr, \"Failed to get high SNR: Max = {}\".format(max_snr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
